#ifndef ATTACK_H
#define ATTACK_H
#include <cstdlib>
#include "Player.h"
#include "Monster.h"
#include "AttackType.h"
#include <sstream>
using std::string;
using std::stringstream;
class Player;
class Monster;


namespace Attack{
	/*
	 * doAttack
	 * Description: Player, p, attacks Monster, m, with attack type, a.
	 * Pre: Player and Monster should not be dead. AttackType, a, should be unlocked
	 * Post: Player attacks the monster and reduces its life, unless the monster evades the attack.
	 * 	If the monster's life is reduced to 0 or below, the monster is dead.
	 * Returns: A string detailing the result of the combat
	 */
	static string doAttack(Player* p, Monster* m, const AttackType& a)
	{
		stringstream tmp;
		string message;
		int chanceToEvade = m->getMonsterStats().getAgility() / 2;  // every 2 pts of agility gives 1% chance to evade.
		int chanceForCriticalHit = 7;		// base critical hit chance is 7% for players
		bool criticalHit = false;		// if the player scored a critical hit
		
		if( (rand() % 100) < chanceToEvade )
		{
			tmp << "The " << m->getDesc() << " evaded your " << a.getDesc() << "!";
			message = tmp.str();
			return message;
		}

		int attackTypeBaseDamage = 0;      // assume default attack type base damage is 0.   
		
		int attackActionCost   = a.getActionPointsCost();
		int playerActionPoints = p->getPlayerStats().getActionPoints();
		  
		if( playerActionPoints >= attackActionCost )  // if player has enough AP to peform attack
		{
			int playerActionPointsAfterAttack = playerActionPoints - attackActionCost;
			p->getPlayerStats().setActionPoints( playerActionPointsAfterAttack );
			
			attackTypeBaseDamage = a.getBaseDamage();  // apply attack damage, otherwise defaults to 0.
		}			

		int playerDamageBonus = 0;
		if( p->getCharClass() == WARRIOR ){
			playerDamageBonus = (p->getPlayerStats().getStrength() / 5); 		// WARRIOR gets extra damage boost from strength
			playerDamageBonus += (p->getPlayerStats().getIntelligence() / 20); 	// WARRIOR gets less boost from intelligence
			playerDamageBonus += (p->getPlayerStats().getConcentration() / 10); // WARRIOR get decent boost from concentration
			chanceForCriticalHit += (p->getPlayerStats().getIntelligence() / 5);// WARRIOR get critical hit chance boost from intelligence
		}
		else if( p->getCharClass() == WIZARD ){
			playerDamageBonus = (p->getPlayerStats().getStrength() / 20); 		// WIZARD gets less damage boost from strength
			playerDamageBonus += (p->getPlayerStats().getIntelligence() / 5); 	// WIZARD gets extra damage boost from intelligence
			playerDamageBonus += (p->getPlayerStats().getConcentration() / 10); // WIZARD get decent boost from concentration
			chanceForCriticalHit += (p->getPlayerStats().getStrength() / 5);	// WARRIOR get critical hit chance boost from strength
		}
		else{ // RANGER
			playerDamageBonus = (p->getPlayerStats().getStrength() / 15); 		// RANGER gets ok damage boost from strength
			playerDamageBonus += (p->getPlayerStats().getIntelligence() / 15); 	// RANGER gets ok damage boost from intelligence
			playerDamageBonus += (p->getPlayerStats().getConcentration() / 5); 	// RANGER get extra damage boost from concentration
			chanceForCriticalHit += (p->getPlayerStats().getStrength() / 10);	// RANGER get critical hit chance boost from Strength
			chanceForCriticalHit += (p->getPlayerStats().getIntelligence() / 10);// RANGER get critical hit chance boost from intelligence
		}
		
		int playerDamage   = p->getPlayerStats().getDamage();
		int weightedDamage = playerDamage + attackTypeBaseDamage + playerDamageBonus;
		int mitigateDamage = weightedDamage - m->getMonsterStats().getArmor();
		
		if(mitigateDamage < 0) // can't do negative damage
			mitigateDamage = 0;

		if((rand() % 100) < chanceForCriticalHit) // see if critical hit occured
			criticalHit = true;
		if(criticalHit)
			mitigateDamage = mitigateDamage * 1.5; // add damage for a critical hit

		int monsterHitPointAfterAttack = m->getMonsterStats().getHitPoints() - mitigateDamage;
		m->getMonsterStats().setHitPoints( monsterHitPointAfterAttack );
		
		if( monsterHitPointAfterAttack <= 0 ){
			int mLevel = m->getMonsterStats().getLevel();
			int pLevel = p->getPlayerStats().getLevel();
			if((pLevel - mLevel) >= 2 ){
				p->increaseExp(30);
			}
			else if((pLevel - mLevel == 1)){
				p->increaseExp(25);
			}
			else if((pLevel - mLevel) == 0){
				p->increaseExp(20);
			}
			else if((pLevel - mLevel) == -1){
				p->increaseExp(15);
			}
			else if((pLevel - mLevel) == -2){
				p->increaseExp(10);
			}
			else { // no experience gained, monster is too far below player level
			}
			m->getMonsterStats().setHitPoints(0);
			m->setDead( true );
		}
        
		tmp << "You ";
		if(criticalHit)
			tmp << "critically ";
		tmp << "hit the " << m->getDesc() << " with your " << a.getDesc() << " attack for " << mitigateDamage << " damage!";
		message = tmp.str();
        return message;
	}

	/*
	 * doAttack
	 * Description: Monster, m, attacks Player, p with a random unlocked attack type.
	 * Pre: Player and Monster should not be dead.
	 * Post: Monster attacks the Player and reduces the player's life, unless the player evades the attack.
	 * 	If the player's life is reduced to 0 or below, the player is dead.
	 * Returns: A string detailing the result of the combat
	 */
	static string doAttack(Monster* m, Player* p)
	{
		stringstream tmp;
		string message;
		int chanceToEvade = p->getPlayerStats().getAgility() / 2;  // every 2 pts of agility gives 1% chance to evade.
		int attackTypeBaseDamage = 0;      // assume default attack type base damage is 0.
		bool attackOK = false;		// to make sure monster has enough AP for randomly selected attack
		bool criticalHit = false;		// if the monster scored a critical hit
		int r = 0;			// random attack type
		AttackType anAttackType;
		
		if( (rand() % 100) < chanceToEvade )
		{
			tmp << "You evaded the " << m->getDesc() << "'s attack!";
			message = tmp.str();
			return message;
		}

		if((rand() % 100) < 5)		// critical hit % chance is currently hard-set to 5%
			criticalHit = true;
		
		while(!attackOK){
			r = rand() % m->getNumAttacks();  // random attack type r
			if( m->getAttackType(r, anAttackType) )  // will probably be true all the time, just in case vector.size() = 0;
			{
				message = anAttackType.getDesc();
				int attackActionCost    = anAttackType.getActionPointsCost();
				int monsterActionPoints = m->getMonsterStats().getActionPoints();

				if( monsterActionPoints >= attackActionCost )  // if monster has enough AP to peform attack
				{
					attackOK = true;
					int monsterActionPointsAfterAttack = monsterActionPoints - attackActionCost;
					m->getMonsterStats().setActionPoints( monsterActionPointsAfterAttack );

					attackTypeBaseDamage = anAttackType.getBaseDamage();  // apply attack damage, otherwise defaults to 0.
				}
			}
		}
		
		int weightedDamage = m->getMonsterStats().getDamage() + attackTypeBaseDamage; 
		int mitigateDamage = weightedDamage - p->getPlayerStats().getArmor();
		
		if(mitigateDamage < 0)  // can't do negative damage
			mitigateDamage = 0;

		if(criticalHit)
			mitigateDamage = mitigateDamage * 1.5; // add damage for a critical hit

		int playerHitPointAfterAttack = p->getPlayerStats().getHitPoints() - mitigateDamage;
		p->getPlayerStats().setHitPoints( playerHitPointAfterAttack );
		
		if( playerHitPointAfterAttack <= 0 ){
			p->getPlayerStats().setHitPoints(0);
			p->setDead( true );
		}
        
		tmp << "The " << m->getDesc();
		if(criticalHit)
			tmp << " critically";
		tmp << " hit you with " << anAttackType.getDesc() << " for " << mitigateDamage << " damage!";
		message = tmp.str();
        return message;
	}
}
#endif
